package com.xncoding.async.service;

import org.redisson.api.RFuture;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class RedisLockService {
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 锁的超时时间（秒）
    private static final long LOCK_EXPIRE_TIME = 30;
    public void doBusiness() {
        RLock lock = redissonClient.getLock("lock_key");
        try {
//            lock.lock();
            lock.lock( 20, TimeUnit.SECONDS);
            // 等待获取锁时间100秒，10秒后自动解锁
            boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
            // 执行业务逻辑
            System.out.println("业务执行玩了");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    public void doBusiness1() {
        RLock rLock = redissonClient.getLock("lock_key") ;
        rLock.lock() ;
        RFuture<Boolean> lockFuture = rLock.tryLockAsync(100, 10, TimeUnit.SECONDS);
        lockFuture.whenComplete((res, exception) -> {
            // todo
            rLock.unlockAsync();
        });
    }

    // 获取锁
    public boolean tryLock(String lockKey, String requestId) {
        String lockValue = UUID.randomUUID().toString(); // 使用唯一标识符作为锁的值
        Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
        if (result != null && result) {
            // 将锁的值与requestId关联起来（可选，用于后续解锁时验证）
            // 这里简单起见，我们直接在业务逻辑中依赖锁的自动过期来释放
            // 但在实际场景中，你可能需要更复杂的逻辑来管理锁的持有者和释放时机
            // 例如，使用哈希表来存储lockKey和requestId的映射关系
            // redisTemplate.opsForHash().put("lockMap", lockKey, requestId);
            return true;
        }
        return false;
    }

    // 释放锁（使用Lua脚本确保原子性）
    public void unlock(String lockKey, String lockValue) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                "return redis.call('del', KEYS[1]) " +
                "else " +
                "return 0 " +
                "end";
        redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
                Collections.singletonList(lockKey), lockValue);
    }
    // 注意：上面的unlock方法使用了简单的锁值验证来释放锁，但在实际生产环境中，
    // 你可能需要考虑更复杂的场景，比如锁的自动续期、可重入锁的实现等。
    // 这些功能可以通过扩展RedisLockUtil类或使用第三方库（如Redisson）来实现。


    public void releaseLock(String lockKey, String lockValue) {
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String currentValue = valueOperations.get(lockKey);

        if (currentValue != null && currentValue.equals(lockValue)) {
            // 如果当前值与我们持有的锁值匹配，则删除锁
            redisTemplate.delete(lockKey);
        } else {
            // 否则，不执行任何操作（锁可能被其他客户端持有或已经过期）
            System.out.println("Lock not held by this client, skipping release.");
        }
    }
}
